﻿using System.Collections.Generic;
using System.Data.Common;
using System.Text;

using vJine.Core.IoC;
using System;
using System.Reflection;
using System.ComponentModel;

namespace vJine.Core.ORM {
    /// <summary>
    /// 数据库适配器抽象泛型基类
    /// </summary>
    /// <typeparam name="Tadapter">适配器类型</typeparam>
    public abstract class IDbAdapter<Tadapter> : IDbAdapter where Tadapter:IDbAdapter<Tadapter> {
        /// <summary>
        /// 获取实体类主键集合
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <returns>主键集合</returns>
        public override List<Class<Tentity>.Property> GetKeys<Tentity>() {
            return OrmConfig<Tadapter, Tentity>.Keys;
        }
        /// <summary>
        /// 获取实体类唯一键集合
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <returns>唯一键集合</returns>
        public override Dictionary<string, List<Class<Tentity>.Property>> GetUniques<Tentity>() {
            return OrmConfig<Tadapter, Tentity>.Uniques;
        }
        /// <summary>
        /// 获取实体类指定的唯一键组
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="group_name">键组名称</param>
        /// <returns>唯一键组</returns>
        public override List<Class<Tentity>.Property> GetUniques<Tentity>(string group_name) {
            return OrmConfig<Tadapter, Tentity>.GetUniques(group_name);
        }
        /// <summary>
        /// 获取实体类除主键意外的属性集合
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <returns>属性集合</returns>
        public override List<Class<Tentity>.Property> GetValues<Tentity>() {
            return OrmConfig<Tadapter, Tentity>.Values;
        }
        /// <summary>
        /// 获取实体类属性集合
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <returns>属性集合</returns>
        public override List<Class<Tentity>.Property> GetProperties<Tentity>() {
            return OrmConfig<Tadapter, Tentity>.Fields;
        }
        /// <summary>
        /// 获取实体类属性类型映射集合
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <returns>映射表集合</returns>
        public override List<MapAttribute> GetMap<Tentity>() {
            return OrmConfig<Tadapter, Tentity>.GetMap();
        }
        /// <summary>
        /// 获取实体类指定属性的类型映射
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="p">属性</param>
        /// <returns>映射表</returns>
        public override MapAttribute GetMap<Tentity>(Class<Tentity>.Property p) {
            return OrmConfig<Tadapter, Tentity>.GetMap(p);
        }

        /// <summary>
        /// 获取实体类的插入代理
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="P">属性集合</param>
        /// <returns>插入代理</returns>
        public override Exec<Tentity, DbCommand> Get_I<Tentity>(IList<Class<Tentity>.Property> P) {
            return OrmConfig<Tadapter, Tentity>.Get_I(P);
        }
        /// <summary>
        /// 获取实体类的查询代理
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="P">属性集合</param>
        /// <returns>查询代理</returns>
        public override Exec<DbDataReader, Tentity> Get_Q<Tentity>(IList<Class<Tentity>.Property> P) {
            return OrmConfig<Tadapter, Tentity>.Get_Q(P);
        }
    }
    /// <summary>
    /// 数据库适配器抽象基类
    /// </summary>
    public abstract class IDbAdapter {
        static IDbAdapter() {
            Init_Priorities();
        }
        
        /// <summary>
        /// 运算符及其对应的优先级
        /// </summary>
        static Dictionary<string, int> Priorities = new Dictionary<string, int>();
        static void Init_Priorities() {
            Priorities.Add("*", 1); Priorities.Add("/", 1); Priorities.Add("%", 1);
            Priorities.Add("+", 2); Priorities.Add("-", 2);
            Priorities.Add(">", 3); Priorities.Add(">=", 3); Priorities.Add("<", 3); Priorities.Add("<=", 3);
            Priorities.Add("=", 4); Priorities.Add("!=", 4); //Priorities.Add("=?", 4);
            Priorities.Add("is", 4);
            Priorities.Add("like", 4); Priorities.Add("not like", 4);
            Priorities.Add("and", 5);
            Priorities.Add("or", 6);
        }

        /// <summary>
        /// 数据库连接
        /// </summary>
        public DbConnection Conn { get; internal set; }

        /// <summary>
        /// 适配器名称
        /// </summary>
        public string Name { get; protected set; }

        /// <summary>
        /// 命令参数前缀
        /// </summary>
        public string ParamPrefix { get; protected set; }
        /// <summary>
        /// 获取数据库时间SQL语句
        /// </summary>
        public string GetDateTime { get; protected set; }

        /// <summary>
        /// NULL
        /// </summary>
        public string NULL { get; protected set; }
        /// <summary>
        /// NOT NULL
        /// </summary>
        public string NOT_NULL { get; protected set; }

        /// <summary>
        /// 左引用
        /// </summary>
        public string QuoteOpen { get; set; }
        /// <summary>
        /// 右引用
        /// </summary>
        public string QuoteClose { get; set; }

        /// <summary>
        /// 数据库关键字
        /// </summary>
        public List<string> KeyWords { get; protected set; }

        /// <summary>
        /// 获取实体类主键集合
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <returns>主键集合</returns>
        public virtual List<Class<Tentity>.Property> GetKeys<Tentity>() {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 获取实体类唯一键集合
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <returns>唯一键集合</returns>
        public virtual Dictionary<string, List<Class<Tentity>.Property>> GetUniques<Tentity>() {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 获取实体类指定的唯一键组
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="group_name">键组名称</param>
        /// <returns>唯一键组</returns>
        public virtual List<Class<Tentity>.Property> GetUniques<Tentity>(string group_name) {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 获取实体类除主键意外的属性集合
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <returns>属性集合</returns>
        public virtual List<Class<Tentity>.Property> GetValues<Tentity>() {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 获取实体类属性集合
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <returns>属性集合</returns>
        public virtual List<Class<Tentity>.Property> GetProperties<Tentity>() {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 获取实体类属性类型映射集合
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <returns>映射表集合</returns>
        public virtual List<MapAttribute> GetMap<Tentity>() {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 获取实体类指定属性的类型映射
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="p">属性</param>
        /// <returns>映射表</returns>
        public virtual MapAttribute GetMap<Tentity>(Class<Tentity>.Property p) {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 获取实体类的插入代理
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="P">属性集合</param>
        /// <returns>插入代理</returns>
        public virtual Exec<Tentity, DbCommand> Get_I<Tentity>(IList<Class<Tentity>.Property> P) {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 获取实体类的查询代理
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="P">属性集合</param>
        /// <returns>查询代理</returns>
        public virtual Exec<DbDataReader, Tentity> Get_Q<Tentity>(IList<Class<Tentity>.Property> P) {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 生成创建表数据库命令
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="table_name">表名</param>
        /// <returns>数据库命令</returns>
        public virtual DbCommand PrepareCreate<Tentity>(string table_name) {

            StringBuilder sbCreate =
                new StringBuilder("CREATE TABLE " + (table_name ?? Class<Tentity>.Name) + " (");

            Type t_dto = typeof(Tentity);
            List<Class<Tentity>.Property> P = this.GetProperties<Tentity>();
            for (int i = 0, len_max = P.Count - 1; i <= len_max; i++) {
                Class<Tentity>.Property p_i = P[i];

                Type pType = p_i.pTypeNull != null ? p_i.pTypeNull : p_i.pType;

                MapAttribute map = this.GetMap<Tentity>(p_i);
                if (map == null) {
                    throw new OrmException("Type[{0}] Not Supprot For Create DDL", pType.FullName);
                }

                sbCreate.Append(map.Alias).Append(" ")
                    .Append(map.SQL_TYPE)
                    .Append((p_i.IsNullable || map.IsNullable) ? this.NULL : this.NOT_NULL);

                if (i < len_max) {
                    sbCreate.Append(",");
                }
            }

            //Primary Keys
            List<Class<Tentity>.Property> Keys = this.GetKeys<Tentity>();
            if (Keys.Count > 0) {
                sbCreate.Append(",CONSTRAINT PK_" + table_name + " PRIMARY KEY (");
                for (int i = 0, len_max = Keys.Count - 1; i <= len_max; i++) {
                    sbCreate.Append(this.GetMap<Tentity>(Keys[i]).Alias);
                    if (i < len_max) {
                        sbCreate.Append(",");
                    }
                }
                sbCreate.Append(")");
            }
            //Unique Keys
            Dictionary<string, List<Class<Tentity>.Property>> Uniques = this.GetUniques<Tentity>();
            foreach (KeyValuePair<string, List<Class<Tentity>.Property>> unique in Uniques) {
                sbCreate.Append(",CONSTRAINT UK_" + table_name + "_" + unique.Key + " UNIQUE (");
                List<Class<Tentity>.Property> _u_keys = unique.Value;
                for (int k = 0, len_k = _u_keys.Count - 1; k <= len_k; k++) {
                    Class<Tentity>.Property key_k = _u_keys[k];
                    sbCreate.Append(this.GetMap<Tentity>(key_k).Alias);
                    if (k < len_k) {
                        sbCreate.Append(",");
                    }
                }
                sbCreate.Append(")");
            }

            sbCreate.Append(")");

            DbCommand dbCmd = this.Conn.CreateCommand();
            dbCmd.CommandType = System.Data.CommandType.Text;
            dbCmd.CommandText = sbCreate.ToString();

            return dbCmd;
        }

        /// <summary>
        /// 生成删除表数据库指令
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="IfExists">判断存在</param>
        /// <param name="table_name">表名</param>
        /// <returns>数据库指令</returns>
        public virtual DbCommand PrepareDrop<Tentity>(bool IfExists, string table_name) {

            DbCommand dbCmd = this.Conn.CreateCommand();
            dbCmd.CommandType = System.Data.CommandType.Text;
            if (IfExists) {
                dbCmd.CommandText = "DROP TABLE IF EXISTS " + table_name;
            } else {
                dbCmd.CommandText = "DROP TABLE " + table_name;
            }

            return dbCmd;
        }

        /// <summary>
        /// 创建查询表数据库命令
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="dbCmd">数据库命令</param>
        /// <param name="sbCmd">命令SQL</param>
        /// <param name="P">属性集合</param>
        public virtual void ToSelectString<Tentity>(DbCommand dbCmd, StringBuilder sbCmd, List<Class<Tentity>.Property> P) {
            sbCmd.Append(" ");
            for(int i = 0, len = P.Count - 1; i <= len; i++) {
                MapAttribute map_i = this.GetMap<Tentity>(P[i]);
                sbCmd.Append(map_i.name_alias);
                if(i < len) {
                    sbCmd.Append(",");
                }
            }
        }

        /// <summary>
        /// 创建查询条件数据库命令
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="preCmd">条件语句SQL</param>
        /// <param name="p">条件表达式</param>
        /// <returns>数据库命令</returns>
        public DbCommand ToWhereString<Tentity>(string preCmd, Class<Tentity>.Where p) {
            DbCommand dbCmd = this.Conn.CreateCommand();
            StringBuilder sbCmd = new StringBuilder(preCmd);
            
            if (p != null) {
                sbCmd.Append(" Where ");
                int pCounter = 0;
                this.ToWhereString<Tentity>(dbCmd, sbCmd, p, ref pCounter, false);
            }

            dbCmd.CommandText = sbCmd.ToString();
            return dbCmd;
        }

        /// <summary>
        /// 创建查询条件数据库命令
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="dbCmd">数据库命令</param>
        /// <param name="sbCmd">数据SQL语句</param>
        /// <param name="P">属性</param>
        /// <param name="pCounter">参数技术</param>
        /// <param name="HasBrackets">包含括号</param>
        public virtual void ToWhereString<Tentity>(
            DbCommand dbCmd,
            StringBuilder sbCmd, Class<Tentity>.Property P, ref int pCounter, bool HasBrackets) {
            Class<Tentity>.Property L = P.L as Class<Tentity>.Property;
            Class<Tentity>.Property R = P.R as Class<Tentity>.Property;

            if (R == null && L == null) {
                return;
            }

            if (HasBrackets) {
                sbCmd.Append("(");
            }
            {
                if (L.Op == null) {
                    sbCmd.Append(this.GetMap<Tentity>(L).Alias); //*
                } else {
                    this.ToWhereString(
                        dbCmd,
                        sbCmd, L, ref pCounter, Priorities[P.Op] < Priorities[L.Op]);
                }

                sbCmd.Append(" ").Append(P.Op).Append(" ");

                if (R != null) {
                    if (R.Op == null) {
                        sbCmd.Append(this.GetMap<Tentity>(R).Alias); //*
                    } else {
                        this.ToWhereString(
                            dbCmd,
                            sbCmd, R, ref pCounter, Priorities[P.Op] < Priorities[R.Op]);
                    }
                } else {
                    if (P.Op == "is") {
                        sbCmd
                            .Append(P.R.ToString());
                    } else {
                        string ParamName =
                                this.ParamPrefix + "P_" + pCounter++.ToString();
                        sbCmd.Append(ParamName);

                        if (dbCmd != null) {
                            DbParameter param = dbCmd.CreateParameter();
                            param.ParameterName = ParamName;

                            MapAttribute map = this.GetMap<Tentity>(L);
                            param.Value = 
                                map.Conv_I != null ? map.Conv_I.Invoke(null, new object[] { P.R }) : param.Value = P.R;

                            P.PropertyChanged += (object sender, PropertyChangedEventArgs e)=> {
                                if(e.PropertyName == "R") {
                                    param.Value =
                                        map.Conv_I != null ? map.Conv_I.Invoke(null, new object[] { P.R }) : param.Value = P.R;
                                }
                            };

                            dbCmd.Parameters.Add(param);
                        }
                    }
                }
            }
            if (HasBrackets) {
                sbCmd.Append(")");
            }
        }

        /// <summary>
        /// 生成更新数据库命令
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="dbCmd">数据库命令</param>
        /// <param name="sbCmd">数据库SQL语句</param>
        /// <param name="P">属性</param>
        /// <param name="pCounter">参数计数</param>
        public virtual void ToUpdateString<Tentity>(DbCommand dbCmd, StringBuilder sbCmd, Class<Tentity>.Property P, ref int pCounter) {
            Class<Tentity>.Property L = P.L as Class<Tentity>.Property;
            Class<Tentity>.Property R = P.R as Class<Tentity>.Property;

            if (R == null && L == null) {
                sbCmd.Append(P.Name);
            } else {
                if (L.Op == null) {
                    sbCmd.Append(this.GetMap<Tentity>(L).Alias); //*
                } else {
                    this.ToUpdateString(dbCmd, sbCmd, L, ref pCounter);
                }

                sbCmd.Append(P.Op).Append(" ");

                if (R != null) {
                    if (R.Op == null) {
                        sbCmd.Append(this.GetMap<Tentity>(R).Alias); //*
                    } else {
                        this.ToUpdateString<Tentity>(dbCmd, sbCmd, R, ref pCounter);
                    }
                } else {
                    string ParamName = this.ParamPrefix + "U_" + pCounter++.ToString();
                    sbCmd.Append(ParamName);

                    if (dbCmd != null) {
                        DbParameter param = dbCmd.CreateParameter();
                        param.ParameterName = ParamName;
                        MapAttribute map = this.GetMap<Tentity>(L);
                        param.Value =
                                map.Conv_I != null ? map.Conv_I.Invoke(null, new object[] { P.R }) : param.Value = P.R;
                        if (param.Value == null) {
                            param.Value = DBNull.Value;
                        }

                        P.PropertyChanged += (object sender, PropertyChangedEventArgs e) => {
                            if(e.PropertyName == "R") {
                                param.Value =
                                    map.Conv_I != null ? map.Conv_I.Invoke(null, new object[] { P.R }) : param.Value = P.R;
                                if(param.Value == null) {
                                    param.Value = DBNull.Value;
                                }
                            }
                        };

                        dbCmd.Parameters.Add(param);
                    }
                }
            }
        }

        /// <summary>
        /// 创建插入数据库命令
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="table_name">表名</param>
        /// <param name="Keys">主键属性集合</param>
        /// <returns>数据库命令</returns>
        public virtual DbCommand PrepareInsert<Tentity>(string table_name, IList<Class<Tentity>.Property> Keys) {
            StringBuilder sbCmd = new StringBuilder();
            DbCommand dbCmd = this.Conn.CreateCommand();

            for (int j = 0, len_j = Keys.Count - 1; j <= len_j; j++) {
                MapAttribute map_j = this.GetMap<Tentity>(Keys[j]);
                DbParameter dbParam = dbCmd.CreateParameter();
                dbParam.ParameterName = this.ParamPrefix + map_j.Alias; //*
                if(map_j.DbType != null) {
                    dbParam.DbType = map_j.DbType.Value;
                }
                dbCmd.Parameters.Add(dbParam);
            }

            DbParameterCollection db_params = dbCmd.Parameters;
            if(string.IsNullOrEmpty(table_name)) {
                sbCmd.Append("Insert Into {0} (");
            } else {
                sbCmd.Append("Insert Into ").Append(table_name).Append(" (");
            }
            for(int j = 0, len_j = Keys.Count - 1; j <= len_j; j++) {
                sbCmd.Append(this.GetMap<Tentity>(Keys[j]).Alias); //*

                if(j < len_j) {
                    sbCmd.Append(",");
                }
            }
            sbCmd.Append(") Values (");
            for(int j = 0, len_j = db_params.Count - 1; j <= len_j; j++) {
                sbCmd.Append(db_params[j].ParameterName);

                if(j < len_j) {
                    sbCmd.Append(",");
                }
            }
            sbCmd.Append(")");

            dbCmd.CommandText = sbCmd.ToString();
            return dbCmd;
        }

        /// <summary>
        /// 创建删除数据库命令
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="table_name">表名</param>
        /// <param name="Where">条件表达式</param>
        /// <returns>数据库命令</returns>
        public virtual DbCommand PrepareDelete<Tentity>(string table_name, Class<Tentity>.Where Where) {
            StringBuilder sbCmd = new StringBuilder();
            DbCommand dbCmd = this.Conn.CreateCommand();
            int pCounter = 0;

            sbCmd.Append("Delete From ").Append(table_name);

            if(Where != null) {
                sbCmd.Append(" Where ");
                this.ToWhereString<Tentity>(
                    dbCmd, sbCmd, Where, ref pCounter, false);
            }

            dbCmd.CommandText = sbCmd.ToString();
            return dbCmd;
        }

        /// <summary>
        /// 创建更新数据库命令
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="table_name">表名</param>
        /// <param name="Set">更新表达式</param>
        /// <param name="Where">条件表达式</param>
        /// <returns>数据库命令</returns>
        public virtual DbCommand PrepareUpdate<Tentity>(string table_name, Class<Tentity>.Set Set, Class<Tentity>.Where Where) {
            StringBuilder sbCmd = new StringBuilder();

            int pCounter = 0;
            DbCommand dbCmd = this.Conn.CreateCommand();

            sbCmd.Append("Update ").Append(table_name).Append(" Set ");
            //Set
            this.ToUpdateString<Tentity>(dbCmd, sbCmd, Set, ref pCounter);

            //Where
            if(Where != null) {
                sbCmd.Append(" Where ");
                this.ToWhereString<Tentity>(
                    dbCmd, sbCmd, Where, ref pCounter, false);
            }

            dbCmd.CommandText = sbCmd.ToString();
            return dbCmd;
        }

        /// <summary>
        /// 生成查询数据库命令
        /// </summary>
        /// <typeparam name="Tentity">实体类型</typeparam>
        /// <param name="max">最大记录数</param>
        /// <param name="fields">属性集合（要查询的属性）</param>
        /// <param name="table_name">表名</param>
        /// <param name="where">条件表达式</param>
        /// <param name="orders">排序字段</param>
        /// <returns></returns>
        public virtual DbCommand PrepareQuery<Tentity>(int max, List<Class<Tentity>.Property> fields, string table_name, Class<Tentity>.Where where, List<Class<Tentity>.Property> orders) {
            int pCounter = 0;
            StringBuilder sbCmd = new StringBuilder();
            DbCommand dbCmd = this.Conn.CreateCommand();

            sbCmd.Append("Select");

            if(max > 0) {
                sbCmd.Append(" Top ").Append(max).Append(" ");
            }

            //Fields
            this.ToSelectString<Tentity>(dbCmd, sbCmd, fields);

            sbCmd.Append(" From ").Append(table_name);

            //Where
            if(where != null) {
                sbCmd.Append(" Where ");
                this.ToWhereString<Tentity>(
                    dbCmd, sbCmd, where, ref pCounter, false);
            }

            //Order By
            if(orders != null && orders.Count > 0) {
                sbCmd.Append(" Order By ");
                for(int j = 0, len_j = orders.Count - 1; j <= len_j; j++) {
                    Class<Tentity>.Property order_j = orders[j];
                    if(order_j.IsASC == null) {
                        continue;
                    }
                    sbCmd.Append(order_j.Name).Append(order_j.IsASC.Value ? " ASC" : " DESC");
                    if(j < len_j) {
                        sbCmd.Append(",");
                    }
                }
            }

            dbCmd.CommandText = sbCmd.ToString();

            return dbCmd;
        }
    }
}